package server.loader;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;

public class WebappClassLoader extends URLClassLoader {
    //应用名
    private String contextName = null;
    private String classesPath = null;
    private String libPath = null;
    private Set<String> jars = new HashSet<>();
    private String webXmlPath = null;
    private long webXmlLastModified = -1;
    //缓存加载的类信息
    private Map<String, ClassInfo> cache = new HashMap<>();
    public WebappClassLoader(String context) {
        super(new URL[0]);
        //context是应用名
        this.contextName = context;
        //应用路径
        String appPath = System.getProperty("user.dir") + File.separator + "webapps" + File.separator + context + File.separator;
        //查看是否存在该目录
        File appFile = new File(appPath);
        if (!appFile.exists() || !appFile.isDirectory())
            return;
        //配置加载class文件
        configClass(appPath);
        //配置加载jar包
        configJar(appPath);
        initWebXmlFileState(appPath);
    }
    //配置jar文件搜索路径
    private void configJar(String path) {
        libPath = path + "WEB-INF" + File.separator + "lib" + File.separator;
        File libFile = new File(libPath);
        //不存在文件夹
        if (!libFile.exists() || !libFile.isDirectory())
            return;
        //无内容
        File[] files = libFile.listFiles();
        if (files == null)
            return;
        //遍历子目录,找jar包
        for (File f : files) {
            if (f.getName().endsWith(".jar")) {
                try {
                    //将jar路径加到url中
                    String jarPath = libPath + f.getName();
                    URL url = new URL("file:" + jarPath);
                    jars.add(jarPath);
                    super.addURL(url);
                } catch (Exception e) {

                }
            }

        }
    }
    //配置class文件搜索路径
    private void configClass(String path) {
        classesPath = path + "WEB-INF" + File.separator +"classes" + File.separator;

        File classesFile = new File(classesPath);
        //不存在文件夹
        if (!classesFile.exists() || !classesFile.isDirectory())
            return;
        //添加classes文件搜索路径
        try {
            URL url = new URL("file:" + classesPath);
            super.addURL(url);
        } catch (Exception e) {

        }
    }
    //初始化xml文件的状态信息
    public void initWebXmlFileState(String path) {
        webXmlPath = path + "WEB-INF" + File.separator +"web.xml";
        File file = new File(webXmlPath);
        webXmlLastModified = file.lastModified();
    }

    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        //从缓存中拿数据
        if (cache.get(name) != null)
            return cache.get(name).clazz;
        Class<?> clazz = null;
        try {
            clazz = ClassLoader.getSystemClassLoader().loadClass(name);
            if (clazz != null) {
                if (resolve)
                    resolveClass(clazz);
                return clazz;
            }
        } catch (ClassNotFoundException e) {

        }

        //类加载器加载
        clazz = super.loadClass(name, resolve);
        //没找到,抛出异常
        if (clazz == null)
            throw new ClassNotFoundException();
        //添加缓存
        ClassInfo classInfo = new ClassInfo();
        classInfo.name = name;
        classInfo.clazz = clazz;

        String loadPath = name.replace('.', '/').concat(".class");
        File classFile = new File(classesPath, loadPath);
        //类是从classes目录下加载的
        if (classFile.exists() && classFile.isFile()) {
            classInfo.lastModified = classFile.lastModified();
            classInfo.path = classFile.getAbsolutePath();
            cache.put(name, classInfo);
        }

        //类是从jar包下加载的
        File libFile = new File(libPath);
        if (libFile.exists() && libFile.isDirectory()) {
            File[] files = libFile.listFiles();
            if (files != null) {
                for (File f : files) {
                    if (f.getName().endsWith(".jar")) {
                        try {
                            JarFile jarFile = new JarFile(f);
                            if (jarFile.getEntry(loadPath) == null)
                                continue;
                            classInfo.lastModified = f.lastModified();
                            classInfo.path = f.getAbsolutePath();
                            cache.put(name, classInfo);
                            break;
                        } catch (Exception e) {

                        }

                    }

                }
            }
        }

        log("加载[" + name + "]成功, 缓存对象为" + classInfo.toString());
        return clazz;
    }

    //判断是否有class文件,jar文件或web.xml文件被修改
    public boolean modified() {
        addNewJars();
//        return (xmlModified() || classModified());
        return classModified();
    }

    private void addNewJars() {
        File libFile = new File(libPath);
        //将新加入的jar文件加入加载器加载路径
        if (libFile.exists() && libFile.isDirectory()) {
            File[] files = libFile.listFiles();
            if (files == null)
                return;
            //遍历子目录,找jar包
            for (File f : files) {
                if (f.getName().endsWith(".jar")) {
                    try {
                        String jarPath = libPath + f.getName();
                        if (!jars.contains(jarPath)) {
                            //将jar路径加到url中
                            URL url = new URL("file:" + jarPath);
                            super.addURL(url);
                            jars.add(jarPath);
                            log("新的jar包{" + f.getName() + "}加入到加载路径");
                        }
                    } catch (Exception e) {

                    }
                }

            }
        }
    }
    private boolean xmlModified() {
        File file = new File(webXmlPath);
        long newModified = file.lastModified();
        //之前没有xml文件,后边新加了
        if (webXmlLastModified == 0 && newModified > 0)
            return true;
        //之前有xml文件,后边修改过
        if (webXmlLastModified > 0 && webXmlLastModified < newModified)
            return true;
        //之前有xml文件,后边删除了
        if (webXmlLastModified > 0 && newModified == 0)
            return true;
        return false;
    }

    private boolean classModified() {
        for (ClassInfo classInfo : cache.values()) {
            File file = new File(classInfo.path);
            //如果文件被删除或修改,返回TRUE
            long modified = file.lastModified();
            if (modified == 0 || modified > classInfo.lastModified) {
                log("文件{" + file.getName() + "}被修改或删除");
                return true;
            }

        }
        return false;
    }

    private void log(String message) {
        System.out.println("WebappClassLoader: " + message);
    }

}
